package base

import (
	"fmt"
	"sync"
)

func ConditionVarLock() {
	// 以简单的邮箱通知为例
	// 定义邮箱状态, 0: 没有邮件, 1: 有新邮件
	var mailbox uint8
	// 定义一个 读写锁类型变量, 作为邮件的锁
	var lock sync.RWMutex

	// 创建了两个代表条件变量的变量, 它们都是*sync.Cond类型的(需要先创建指针值, 需要 一个sync.Locker类型的参数值)
	// sync.Locker其实是一个接口, 它的声明中只包含了两个方法定义, 即：Lock()和Unlock(), ync.Mutex类型和sync.RWMutex类型都拥有Lock方法和Unlock方法, 只不过它们都是指针方法, 所以这两个类型的指针类型才是sync.Locker接口的实现类型
	// 把 lock 变量的引用值传给 sync.NewCond, lock变量的Lock方法和Unlock方法分别用于对其中写锁的锁定和解锁(sendCond 对共享资源的写操作)
	sendCond := sync.NewCond(&lock)
	// recvCond变量代表的是专门为获取邮件而准备的条件变量,sync.RWMutex 的 RLocker 是 RLock和RUnlock 的实现类型, 所有满足 sync.NewCond 的参数条件
	recvCond := sync.NewCond(lock.RLocker())

	mailbox = 1

	// 发送邮件
	go func() {
		// 先进行互斥锁锁定
		lock.Lock()
		// 一直判断邮箱状态, 所有使用 for 来判断, 如果有邮件则等待取走
		for mailbox == 1 {
			sendCond.Wait()
		}
		// 如果没有邮件则 将邮箱状态 改为 1, 并解除互斥锁
		mailbox = 1
		lock.Unlock()
		// 给接受者发送有新邮件通知(需要等互斥锁解除后在通知, 不然可能会出现意外错误)
		recvCond.Signal()
		fmt.Println(`我已经发送新邮件`)
	}()

	// 接收邮件
	go func() {
		// 先进行读锁锁定
		lock.RLock()
		// 一直判断邮箱状态, 所有使用 for 来判断, 如果没有邮件则等待新邮件
		for mailbox == 0 {
			sendCond.Wait()
		}
		// 如果有邮件则 将邮箱状态 改为 0, 表示取走, 并解除读锁
		mailbox = 0
		lock.RUnlock()
		// 给发送者发送取邮件通知
		sendCond.Signal()
		fmt.Println(`我已经取走新邮件`)
	}()
}